import EventEmitter from '../vendor/events';
import Buffer  from '../vendor/Buffer';
import Q from '../vendor/q';
import helper  from '../vendor/helper';
import u  from '../vendor/underscore';
import util  from '../vendor/util';
import H  from './headers';
import Querystring from '../vendor/querystring';
/**
 * The HttpClient
 *
 * @constructor
 * @param {Object} config The http client configuration.
 */
function HttpClient(config) {
    EventEmitter.call(this);
    this.config = config;
    /**
     * http(s) request object
     * @type {Object}
     */
    this._req = null;
}

util.inherits(HttpClient, EventEmitter);

/**
 * Send Http Request
 * @param {string} httpMethod GET,POST,PUT,DELETE,HEAD
 * @param {string} path The http request path.
 * @param {(string|Buffer|stream.Readable)=} body The request body. If `body` is a
 * stream, `Content-Length` must be set explicitly.
 * @param {Object=} headers The http request headers.
 * @param {Object=} params The querystrings in url.
 * @param {function():string=} signFunction The `Authorization` signature function
 * @param {stream.Writable=} outputStream The http response body.
 * @param {number=} retry The maximum number of network connection attempts.
 * @resolve {{http_headers:Object,body:Object}}
 * @reject {Object}
 * @return {Q.defer}
 */
HttpClient.prototype.sendRequest = function (httpMethod, path, body, headers, params,
                                             signFunction, outputStream) {                                      
    var requestUrl = this._getRequestUrl(path, params);
    var defaultHeaders = {};
    defaultHeaders[H.X_BCE_DATE] = helper.toUTCString(new Date());
  defaultHeaders[H.CONTENT_TYPE] = 'application/json;charset=UTF-8';
    defaultHeaders[H.HOST] = /^\w+:\/\/([^\/]+)/.exec(this.config.endpoint)[1];
    var requestHeaders = u.extend(defaultHeaders, headers);
    // Check the content-length
    if (!requestHeaders.hasOwnProperty(H.CONTENT_LENGTH)) {
        var contentLength = this._guessContentLength(body);
        if (!(contentLength === 0 && /GET|HEAD/i.test(httpMethod))) {
            // 如果是 GET 或 HEAD 请求，并且 Content-Length 是 0，那么 Request Header 里面就不要出现 Content-Length
            // 否则本地计算签名的时候会计算进去，但是浏览器发请求的时候不一定会有，此时导致 Signature Mismatch 的情况
            requestHeaders[H.CONTENT_LENGTH] = contentLength;
        }
    }
    var self = this;
    var createSignature = signFunction || u.noop;
    try {
        return Q.resolve(createSignature(this.config.credentials, httpMethod, path, params, requestHeaders))
            .then(function (authorization, xbceDate) {
                if (authorization) {
                    requestHeaders[H.AUTHORIZATION] = authorization;
                }
                if (xbceDate) {
                    requestHeaders[H.X_BCE_DATE] = xbceDate;
                }
                return self._doRequest(httpMethod, requestUrl,
                    u.omit(requestHeaders, H.CONTENT_LENGTH, H.HOST),
                    body, outputStream);
            });
    }
    catch (ex) {
        return Q.reject(ex);
    }
};

HttpClient.prototype._doRequest = function (httpMethod, requestUrl, requestHeaders, body, outputStream) {
    var deferred = Q.defer();
    var self = this;
    if(requestHeaders[H.CONTENT_TYPE]){
      if (typeof body == 'string' && /application\/json/.test(requestHeaders[H.CONTENT_TYPE].toLowerCase())){
        body = JSON.parse(body);
      }
      requestHeaders[H.CONTENT_TYPE.toLowerCase()] = requestHeaders[H.CONTENT_TYPE];
      delete requestHeaders[H.CONTENT_TYPE];
    }
    let xhr = wx.request({
      url: requestUrl,
      method: httpMethod,
      header: requestHeaders,
      data : body == null ? '' : body,
      success: function (res) {
        let data = res.data,statusCode = res.statusCode,header = res.header;
        if (statusCode === 1223) {
          statusCode = 204;
        }
        var contentType = header['content-type'];
        var isJSON = /application\/json/.test(contentType);
        var responseBody = isJSON ? JSON.parse(data) : data;
        if (!responseBody) {
          responseBody = {
            location: requestUrl
          };
        }
        var isSuccess = statusCode >= 200 && statusCode < 300 || statusCode === 304;
        if (isSuccess) {
          var headers = {};
          if(u.isObject(header)){
            for(var key in header){
              headers[key.toLowerCase()] = typeof header[key] == 'string' ? header[key].replace(/^["|'](.*)["|']$/,"$1") : header[key];
            }
          }
          deferred.resolve({
            http_headers: headers,
            body: responseBody
          });
        }
        else {
          deferred.reject({
            status_code: statusCode,
            message: responseBody.message || '<message>',
            code: responseBody.code || '<code>',
            request_id: responseBody.requestId || '<request_id>'
          });
        }
      },
      fail: function (error) {
        deferred.reject(error);
      }
    });
    // if (xhr.upload) {
    //     u.each(['progress', 'error', 'abort'], function (eventName) {
    //         xhr.upload.addEventListener(eventName, function (evt) {
    //             if (typeof self.emit === 'function') {
    //                 self.emit(eventName, evt);
    //             }
    //         }, false);
    //     });
    // }
    // xhr.send(body);
    self._req = {xhr: xhr};
    return deferred.promise;
};

HttpClient.prototype._guessContentLength = function (data) {
    if (data == null || data === '') {
        return 0;
    }
    else if (u.isString(data)) {
        return Buffer.byteLength(data);
    }
    else if (typeof Blob !== 'undefined' && data instanceof Blob) {
        return data.size;
    }
    else if (typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) {
        return data.byteLength;
    }

    throw new Error('No Content-Length is specified.');
};

HttpClient.prototype._fixHeaders = function (headers) {
    var fixedHeaders = {};

    if (headers) {
        u.each(headers.split(/\r?\n/), function (line) {
            var idx = line.indexOf(':');
            if (idx !== -1) {
                var key = line.substring(0, idx).toLowerCase();
                var value = line.substring(idx + 1).replace(/^\s+|\s+$/, '');
                if (key === 'etag') {
                    value = value.replace(/"/g, '');
                }
                fixedHeaders[key] = value;
            }
        });
    }

    return fixedHeaders;
};

HttpClient.prototype.buildQueryString = function (params) {
    var urlEncodeStr = Querystring.stringify(params);
    // https://en.wikipedia.org/wiki/Percent-encoding
    return urlEncodeStr.replace(/[()'!~.*\-_]/g, function (char) {
        return '%' + char.charCodeAt().toString(16);
    });
};

HttpClient.prototype._getRequestUrl = function (path, params) {
    var uri = path;
    var qs = this.buildQueryString(params);
    if (qs) {
        uri += '?' + qs;
    }
    return this.config.endpoint + uri;
};

export default  HttpClient;

